package cn.lxh.ymcc.service.impl;

import cn.lxh.ymcc.doc.CourseDoc;
import cn.lxh.ymcc.dto.CourserSearchDto;
import cn.lxh.ymcc.hightmapper.HighlightResultMapper;
import cn.lxh.ymcc.repository.CourseEsRepository;
import cn.lxh.ymcc.result.PageList;
import cn.lxh.ymcc.service.ICourseEsService;
import cn.lxh.ymcc.vo.AggrsBucket;
import cn.lxh.ymcc.vo.EsAggsPageList;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;

import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilders;

import org.elasticsearch.search.aggregations.bucket.terms.ParsedStringTerms;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.sort.FieldSortBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.aggregation.AggregatedPage;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;


/**
 * 实现高亮： 1.设置高亮样式  2.处理映射结果 准备映射器拷贝mapper使用  3.使用映射器处理结果
 *两个注入 一个hight亮度mapper  一个EStemplte
 *
 * 聚合：
 *    -1.设置搜索条件   2.处理搜索结果成前端需要的数据结构
 *
 */
@Service
public class CourseEsServiceImpl implements ICourseEsService {
    @Autowired
    private CourseEsRepository courseEsRepository;

    //mapper的结果
    @Autowired
    private HighlightResultMapper highlightResultMapper;
    //使用es的搜索注入
    @Autowired
    private ElasticsearchRestTemplate elasticsearchRestTemplate;

    /**
     * 增加到es中
     * @param doc
     */
    @Override
    public void saveCourse(CourseDoc doc) {
        courseEsRepository.save(doc);

    }

    /**
     * 删除
     * @param doc
     */
    @Override
    public void deleteCourse(CourseDoc doc) {
        courseEsRepository.delete(doc);
    }

    /**
     * 前端 传过来的搜索条件
     * @param dto
     * @return
     */
    @Override
    public PageList<CourseDoc> search(CourserSearchDto dto) {

        //一定要这个 builder 有所有的规则
        NativeSearchQueryBuilder builder = new NativeSearchQueryBuilder();


//        防御性编程 排序规则======================用Switch给每个 sortfile赋值====第一个升序降序开始
        if(StringUtils.hasLength(dto.getSortField())) {
            // dto.getSortField()  xl rq  xp pl
            String sortField = null;
            switch (dto.getSortField().toLowerCase()) {
                case "xl":
                    sortField = "saleCount";
                    break;
                case "xp":
                    sortField = "onlineTime";
                    break;
                case "pl":
                    sortField = "commentCount";
                    break;
                case "jg":
                    sortField = "price";
                    break;
                case "rq":
                    sortField = "viewCount";
                    break;
                default:
                    break;
            }
//           根据每个sortfiled排序==================注意预防性编程=========升序排序======降序排序
            if (sortField != null) {
                if (StringUtils.hasLength(dto.getSortType())) {
                    SortOrder sortOrder = dto.getSortType().equalsIgnoreCase("ASC") ? SortOrder.ASC : SortOrder.DESC;
                    builder.withSort(new FieldSortBuilder(sortField).order(sortOrder));
                }
            }
        }
//       第一个====getSortField==升序===降序结束
        //封装分页相关的参数===================  0 代表第一页
        builder.withPageable(PageRequest.of(dto.getPage()-1, dto.getRows()));
        //封装查询条件
        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
        //DSL查询：Es提供的查询方法  private String keyword==========关键字查询
        if(StringUtils.hasLength(dto.getKeyword())){
            boolQuery.must(QueryBuilders.matchQuery("name",dto.getKeyword()));
        }
        //DSL过滤-----------------
        if(dto.getCourseTypeId() != null){
            boolQuery.filter(QueryBuilders.termQuery("courseTypeId",dto.getCourseTypeId()));
        }
        if(StringUtils.hasLength(dto.getGradeName())){
            boolQuery.filter(QueryBuilders.termQuery("gradeName",dto.getGradeName()));
        }
        if(StringUtils.hasLength(dto.getChargeName())){
            boolQuery.filter(QueryBuilders.termQuery("chargeName",dto.getChargeName()));
        }
        if(dto.getPriceMin() != null){
            boolQuery.filter(QueryBuilders.rangeQuery("price").gte(dto.getPriceMin()));
        }
        if(dto.getPriceMax() != null){
            boolQuery.filter(QueryBuilders.rangeQuery("price").lte(dto.getPriceMax()));
        }
        //做高亮添加样式------<font red
        builder.withHighlightFields(
                new HighlightBuilder.Field("name")
                        .preTags("<font style='color:red'>")
                        .postTags("</font>"));

        //添加聚合查询条件
        //
        //添加等级名字聚合
        builder.addAggregation(AggregationBuilders.terms("aggsGradeName").field("gradeName"));
        //添加收费规则聚合
        builder.addAggregation(AggregationBuilders.terms("aggsChargeName").field("chargeName"));


        //搜索Es里，里面进行查询
        builder.withQuery(boolQuery);
        NativeSearchQuery build = builder.build();
        //Page<CourseDoc> page = courseEsRepository.search(build);---使用高亮必须使用下面的
        AggregatedPage<CourseDoc> page = elasticsearchRestTemplate.queryForPage(build, CourseDoc.class, highlightResultMapper);


        //处理聚合结果成Map
        //1.拿到所有的聚合结果,整成map结构
        Map<String, Aggregation> aggregationMap = page.getAggregations().asMap();
        ParsedStringTerms aggsGradeName = (ParsedStringTerms) aggregationMap.get("aggsGradeName");
        ParsedStringTerms aggsChargeName = (ParsedStringTerms)aggregationMap.get("aggsChargeName");

        //处理等级聚合结果---vo
        List<AggrsBucket> aggrsBuckets = new ArrayList<>();
        aggsGradeName.getBuckets().forEach(bucket -> {
            String key = bucket.getKeyAsString();
            long docCount = bucket.getDocCount();
            AggrsBucket aggrsBucket = new AggrsBucket(key, docCount);
            aggrsBuckets.add(aggrsBucket);
        });

        //处理收费规则聚合
        List< AggrsBucket> aggrsBuckets1 = new ArrayList<>();
        aggsChargeName.getBuckets().forEach(bucket -> {
            String key = bucket.getKeyAsString();
            long docCount = bucket.getDocCount();
            AggrsBucket aggrsBucket = new AggrsBucket(key, docCount);
            aggrsBuckets1.add(aggrsBucket);
        });

        Map<String, List<AggrsBucket>> map = new HashMap<>();
        map.put("aggsGradeName",aggrsBuckets);
        map.put("aggsChargeName",aggrsBuckets1);

        //pagelist的返回---单独定义 vo-Esaggplist
        return new EsAggsPageList<>(page.getTotalElements(),page.getContent(), map);

    }

}
